package com.cskaoyan._03innerclazz._06lambda._01introduction;

/**
 * @description:
 * @author: 景天
 * @date: 2023/2/13 17:40
 **/
/*
以上描述总体就说了三点：

1. Lambda表达式仍然是局部内部类，是特殊的局部内部类，仍然定义在局部位置。
而且局部内部类的注意事项，也一样存在。
2. Lambda表达式在取代匿名内部类时，不是全部都取代，而是取代接口的匿名内部类，
而类的匿名内部类Lambda表达式是不能取代的。
3. Lambda表达式是匿名内部类的更进一步， Lambda表达式得到的也不是一个类，
而是一个对象，并且是接口的子类对象。


使用前提:
Lambda表达式虽然说是取代接口的匿名内部类，但也不是什么接口都能用Lambda表达式创建子类对象。

Lambda表达式要求的接口中，必须有且仅有一个必须要实现的抽象方法。
这种接口在Java中，被称之为"功能接口"。功能接口在语法上，
可以使用注解@FunctionalInterface标记在接口头上，用于检测一个接口是否是功能接口。

例如：

注解@FunctionalInterface

看完上述关于功能接口的语法定义，思考两个问题：

1. 功能接口中只能有一个方法吗？
2. 功能接口中只能有一个抽象方法吗？

答：

1. 不是，Java8中的默认方法和静态方法不需要子类实现，功能接口中可以允许有它们存在。

2. 不是，有极个别比较特殊的抽象方法，可以不需要子类实现。

   注：Object类是Java每一个类的父类，所以Object类当中的方法实现就可以作为接口抽象方法的实现。比如：

   功能接口不仅有一个抽象方法


lambda 语法:
(形参列表) -> {
// 方法体
}
解释一下：

1. (形参列表)表示功能接口中，必须要重写的抽象方法的形参列表。
2. ->由一个英文横杠 + 英文大于号字符组成，它是Lambda表达式的运算符，读作goes to。
3. { //方法体 }表示功能接口中，必须要重写的抽象方法的，方法体实现。

其实，看完这个Lambda表达式的语法定义，就已经能够解释为什么Lambda表达式
要求接口有且只有一个必须要实现的抽象方法了。因为语法中仅有一套形参列表和方法体，
只能重写一个方法。
而且，这个语法写出来，很明显Lambda表达式只能重写父接口中的抽象方法，是不能自己新增成员的！

在方法等局部位置，写上述语法后，肯定会报错。原因在于Java是强类型语言，任何变量都有它的数据类型，而直接写Lambda表达式语言
编译器是无从得知它的数据类型的——这个Lambda表达式创建了接口的子类对象，到底是哪个接口的子类对象呢？
所以我们需要帮助编译器，明确Lambda表达式所表示的对象的类型，这个过程称之为 "Lambda表达式的类型推断"。

怎么推断呢？在这个推断过程中，需要给编译器提供额外的信息，告诉它Lambda表达式是哪个接口的子类对象。总得来说，常见和常用的有以下三种方式：

1. 直接用父接口引用接收。
由于Lambda表达式表示的子类对象并没有自己独有的成员，所以直接用父类引用接收完全不会有任何问题。

2. 不用引用接收，但是要直接告诉编译器Lambda表达式是哪个接口的子类对象，
语法上有点像强转（但不是）。
   语法：
   ((父接口的名字)Lambda表达式).方法名(实参)

   这种方式有点类似于匿名对象，所以必须直接调用方法，不然会编译语法报错。

3. 借助方法完成类型推断。
   1. 可以借助方法的返回值数据类型完成类型推断，因为方法的返回值编译器已经知道该返回什么对象。
   2. 可以借助方法的形参的数据类型完成类型推断，因为方法的实参编译器已经知道该传入什么对象。
 */
public class Demo {
    public static void main(String[] args) {
        // 1.使用父接口进行接收
        IA ia = () -> {
            System.out.println("IA 接口中的m1方法的实现");
        };
        ia.m1();

        // 2 .类似强制准换,没有使用引用接收
        //((父接口的名字)Lambda表达式).方法名(实参)
        ((IA) () -> {
            System.out.println("m1方法的实现");
        }).m1();

        // 方法调用
        func2(()->{
            System.out.println("方法通过lambda传递了一个IA的子类对象");
        });
    }

    // 方法的类型推断
    //    1. 可以借助方法的返回值数据类型完成类型推断，
    //    因为方法的返回值编译器已经知道该返回什么对象。
    public static IA func1() {
        // 使用lambda
        return ()->{
            System.out.println("返回值类型是IA类型");
        };
    }

    //    2. 可以借助方法的形参的数据类型完成类型推断，
    //    因为方法的实参编译器已经知道该传入什么对象。
    public static void func2(IA ia) {
        ia.m1();
    }


}

//用于检测一个接口是否是功能接口。
@FunctionalInterface
interface IA{
    void m1();

    //void m5();
    default void m2() {

    }

    default void m3() {

    }

    static void m4() {

    }

    boolean equals(Object object);

}